//#define USE_PLAYERPREFS

using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

public class lwGameSave
{
	private bool 	m_bUsePlayerPrefs = false;
	
	private Dictionary<string,string> m_savedData = null;
#if !UNITY_IPHONE && !UNITY_ANDROID
	private string m_sDistribName = ""; 
	private string m_sGameSeriesName = ""; 
#endif
	
	public lwGameSave( string sDistribName, string sGameSeriesName )
	{
		Init( sDistribName, sGameSeriesName, false );
	}
	
	public lwGameSave( string sDistribName, string sGameSeriesName, bool bUsePlayerPrefs )
	{
		Init( sDistribName, sGameSeriesName, bUsePlayerPrefs );
	}
	
	private void Init( string sDistribName, string sGameSeriesName, bool bUsePlayerPrefs )
	{
		m_bUsePlayerPrefs = bUsePlayerPrefs;

		if( !m_bUsePlayerPrefs )
		{
#if !UNITY_IPHONE && !UNITY_ANDROID
			m_sDistribName = sDistribName;
			m_sGameSeriesName = sGameSeriesName;
#endif
			if ( m_savedData == null )
			{
				m_savedData = new Dictionary<string, string>();
				CreateFolder();
				Load();
			}
		}
	}
	
	
	
	
	public bool HasKey( string sKey )
	{
		if( m_bUsePlayerPrefs )
			return PlayerPrefs.HasKey( sKey );
		else
			return ( m_savedData!=null && m_savedData.ContainsKey( sKey ) );
	}
	
	public int GetInt( string sKey, int nDefault )
	{
		if( m_bUsePlayerPrefs )
			return PlayerPrefs.GetInt( sKey, nDefault );
		else
		{
			int nValue = nDefault;
			if ( m_savedData != null )
			{
				string sValue;
				m_savedData.TryGetValue( sKey, out sValue );
				if ( sValue	!= null && sValue != string.Empty )
					nValue = int.Parse( sValue );
			}
			return nValue;
		}
	}
	
	public void SetInt( string sKey, int nValue )
	{
		if( !m_bUsePlayerPrefs )
		{
			if ( m_savedData != null )
				m_savedData[ sKey ] = nValue.ToString();
			Save();
		}
		else
		{
			PlayerPrefs.SetInt( sKey, nValue );
		}
	}
	
	private float GetFloat( string sKey, float fDefault )
	{
		if( !m_bUsePlayerPrefs )
		{
			float fValue = fDefault;
			if ( m_savedData != null )
			{
				string sValue;
				m_savedData.TryGetValue( sKey, out sValue );
				if ( sValue	!= null && sValue != string.Empty )
					fValue = float.Parse( sValue );
			}
			return fValue;
		}
		else
		{
			return PlayerPrefs.GetFloat( sKey, fDefault );
		}
	}
	
	private void SetFloat( string sKey, float fValue )
	{
		if( !m_bUsePlayerPrefs )
		{
			if ( m_savedData != null )
				m_savedData[ sKey ] = fValue.ToString();
			Save();
		}
		else
		{
			PlayerPrefs.SetFloat( sKey, fValue );
		}
	}

	private string GetString( string sKey, string sDefault )
	{
		if( !m_bUsePlayerPrefs )
		{
			string sValue = sDefault;
			if ( m_savedData != null )
			{
				m_savedData.TryGetValue( sKey, out sValue );
				if ( sValue	== null )
					sValue = string.Empty;
			}
			return sValue;
		}
		else
		{
			return PlayerPrefs.GetString( sKey, sDefault );
		}
	}
	
	private void SetString( string sKey, string sValue )
	{
		if( !m_bUsePlayerPrefs )
		{
			if ( m_savedData != null )
				m_savedData[ sKey ] = sValue;
			Save();
		}
		else
		{
			PlayerPrefs.SetString( sKey, sValue );
		}
	}
	
	private string GetFileName()
	{
		return GetFileName( false );
	}
	
	private string GetFileName( bool bSerialized)
	{
		
#if UNITY_IPHONE || UNITY_ANDROID
		string sAppData = Application.persistentDataPath;
#else
		string sAppData = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
		sAppData += "/" + m_sDistribName + "/" + m_sGameSeriesName; 
#endif
		
		if( m_bUsePlayerPrefs )
			return "";
		
		sAppData += "/SavedData" 
			+ "_" + lwBuildSettings.GetGameVersionName()
			+ (lwBuildSettings.IsSurveyBuild()?"_Survey":"")
			+ (bSerialized ? "_bin" : "" )
			+ ".sav";
		return sAppData;
	}

	private void CreateFolder()
	{
		if( m_bUsePlayerPrefs )
			return;
#if !UNITY_IPHONE && !UNITY_ANDROID
		string sAppData = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
		sAppData += "/" + m_sDistribName;
		if ( !System.IO.Directory.Exists( sAppData ) )
				System.IO.Directory.CreateDirectory( sAppData );
		sAppData += "/" + m_sGameSeriesName;
		if ( !System.IO.Directory.Exists( sAppData ) )
				System.IO.Directory.CreateDirectory( sAppData );
#endif
	}
	
	private void Save()
	{
		if( m_bUsePlayerPrefs )
			return;
		
		if ( m_savedData != null )
		{
			string sFileName = GetFileName();
		   	StreamWriter sw = new StreamWriter ( sFileName );
			sw.WriteLine( "[" + lwBuildSettings.GetGameVersionName() + "]" );
			if ( sw == null )
			{
				Debug.LogError( "Can't save data to file '" + GetFileName() + "'" );
			}
			else
			{
        		foreach( KeyValuePair<string,string> entry in m_savedData )
				{
            		sw.WriteLine( entry.Key + "=" + entry.Value );
				}
	        	sw.Close ();
			}
		}
	}
	
	private void Load()
	{
		if( m_bUsePlayerPrefs )
			return;
		
		if ( m_savedData != null )
		{
        	string sLine = "-";
        	int nOffset;
			string sFileName = GetFileName();
			if ( !System.IO.File.Exists( sFileName ) )
			    Save();
            StreamReader sr = new StreamReader( sFileName );
			if ( sr == null )
			{
				Debug.Log( "Can't load data from file '" + GetFileName() + "', trying to create the file..." );
			}
			else
			{
        		while ( (sLine = sr.ReadLine()) != null) 
				{
            		nOffset = sLine.IndexOf("=");
                	if( nOffset > 0 )
					{
						string sKey = sLine.Substring(0, nOffset);
						string sValue = sLine.Substring(nOffset+1);
                    	m_savedData[ sKey ] = sValue;
					}
                }
 	            sr.Close();
    	        Debug.Log( "Saved data loaded " + GetFileName() );
			}
		}
	}
	
	private string GetPrefix()
	{
		return  "slot" + GetCurrentSlotID().ToString();
	}
	
	public int GetIntValueWithPrefix( string sKey, int nDefault )
	{
		if( HaveCurrentSlotID() )
		{
			return GetInt( GetPrefix() + sKey, nDefault );
		}
		return nDefault;
	}

	public void SetIntValueWithPrefix( string sKey, int nValue )
	{
		if( HaveCurrentSlotID() )
		{
			SetInt( GetPrefix() + sKey, nValue );
		}
	}

	public float GetFloatValueWithPrefix( string sKey, float fDefault )
	{
		if( HaveCurrentSlotID() )
		{
			return GetFloat( GetPrefix() + sKey, fDefault );
		}
		return fDefault;
	}

	public void SetFloatValueWithPrefix( string sKey, float fValue )
	{
		if( HaveCurrentSlotID() )
		{
			SetFloat( GetPrefix() + sKey, fValue );
		}
	}

	
	public bool HaveCurrentSlotID()
	{
		return HasKey("currentSlotID");
	}
	
	public int GetCurrentSlotID()
	{
		return GetInt("currentSlotID", 0 );
	}

	public void SetCurrentSlotID( int nID)
	{
		SetInt( "currentSlotID", nID );
	}
	
	
	public string GetPlayerName( int nId )
	{
		string sName = string.Empty;
		string sKey = "PlayerName" + nId.ToString();
		if ( nId >= 0 && nId <= 3 && HasKey( sKey ) )
		    	sName = GetString( sKey, string.Empty );
		return sName;
	}
	public void SetPlayerName( int nId, string sName )
	{
		string sKey = "PlayerName" + nId.ToString();
		if ( nId >= 0 && nId <= 3 )
		    	SetString( sKey, sName );
	}
	
	/// <summary>
	/// To save complex object, the object MUST be xml serializable
	/// </summary>
	public void SetSerialisableObject( string sKey, object obj, System.Type type )
	{
		XmlSerializer xmlSerializer = new XmlSerializer(type);
		StringWriter stringWriter = new StringWriter();
		xmlSerializer.Serialize(stringWriter, obj);
		string serializedXML = stringWriter.ToString();
		serializedXML = serializedXML.Replace( "\n", "" );
		serializedXML = serializedXML.Replace( "\r", "" );
		serializedXML = serializedXML.Replace( "\t", "" );
		SetString( GetPrefix() + sKey, serializedXML );
	}
	
	
	/// <summary>
	/// To load complex object, the object MUST be xml serializable
	/// </summary>
	public object GetSerializedObject( string sKey, System.Type type )
	{
		string sValue = GetString( GetPrefix() + sKey, "");
		if( sValue != "" )
		{
			XmlSerializer xmlSerializer = new XmlSerializer(type);
			StringReader stringReader = new StringReader( sValue );
			return (xmlSerializer.Deserialize( stringReader ));
		}
		return null;
	}
	
	
	public void Save( ISerializable data )  // Overloaded
	{
		string sFileName = GetFileName( true );
		if( !File.Exists( sFileName ) )	
			CreateFolder();
     	Stream stream = File.Open(sFileName, FileMode.Create);
    	BinaryFormatter bformatter = new BinaryFormatter();
    	bformatter.Binder = new VersionDeserializationBinder(); 
    	bformatter.Serialize(stream, data);
    	stream.Close();
 	}
	
	// Call this to load from a file into "data"
	public object Load( System.Type type )
	{ 
		string sFileName = GetFileName( true );
		object data = Activator.CreateInstance(type);
		if( File.Exists( sFileName ) )
		{
		    Stream stream = File.Open(sFileName, FileMode.Open);
		    BinaryFormatter bformatter = new BinaryFormatter();
		    bformatter.Binder = new VersionDeserializationBinder(); 
		    
			data = ( bformatter.Deserialize(stream) );
		   	stream.Close();
		}
		return data;	
	}   // Overloaded
}

// === This is required to guarantee a fixed serialization assembly name, which Unity likes to randomize on each compile
// Do not change this
public sealed class VersionDeserializationBinder : SerializationBinder 
{ 
    public override Type BindToType( string assemblyName, string typeName )
    { 
        if ( !string.IsNullOrEmpty( assemblyName ) && !string.IsNullOrEmpty( typeName ) ) 
        { 
            Type typeToDeserialize = null; 
		   
            assemblyName = Assembly.GetExecutingAssembly().FullName; 
			 // The following line of code returns the type. 
            typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) ); 
			 
            return typeToDeserialize; 
        } 

        return null; 
    } 
}
